package component

import (
	"github.com/hyperway/pkg/constant"
	"github.com/hyperway/pkg/helper"
	"github.com/hyperway/pkg/logger"
	"github.com/jinzhu/copier"
	"net/http"
)

// RegisterMvc 注册API元数据
// TODO 要处理新增、修改、删除的情况
func (s *Server) RegisterMvc(metadata *EndpointMeta) (isFirst bool) {
	if cache, ok := s.MvcMVCC.Load(metadata.MetaKey); ok {
		mvcc := cache.(*EndpointMVCC)
		mvcc.Update(metadata.Version, metadata)
		isFirst = false
	} else {
		logger.TraceId("注册API").Infow("register mvc", "api", metadata.MetaKey)
		mvcc := NewEndpointMVCC(metadata)
		s.MvcMVCC.Store(metadata.MetaKey, mvcc)
		s.WebListener.AddHandler(metadata.HttpMethod, metadata.HttpPattern, s.Handler(mvcc))
		isFirst = true
	}
	return isFirst
}

// Handler 服务端的请求处理器
func (s *Server) Handler(mvcc *EndpointMVCC) RequestHandlerFunc {
	return func(ctx WebContext) error {
		return s.dispatch(mvcc, ctx)
	}
}

// dispatch 调度器，根据请求的版本号，找到对应的Api元数据，然后执行
func (s *Server) dispatch(mvcc *EndpointMVCC, ctx WebContext) error {
	defer func(traceId string) {
		if err := recover(); err != nil {
			logger.TraceId(traceId).Errorw("panic", "error", err)
		}
	}(ctx.RequestId())
	version := ctx.Header(constant.XVersion)
	var mvc EndpointMeta
	if metadata, ok := mvcc.Lookup(version); ok {
		if err := copier.CopyWithOption(&mvc, metadata, copier.Option{DeepCopy: true}); err != nil {
			return err
		}
	} else {
		return s.HandleNotFound(ctx)
	}

	rctx := NewContext(ctx, &mvc)
	return s.doHandle(rctx)
}

// doHandle 处理器，执行过滤器链，然后执行转发器，最后响应结果
func (s *Server) doHandle(ctx InnerContext) *ServerError {

	next := func(ctx InnerContext) *ServerError {
		// 执行，协议转换
		return s.handle(ctx)
	}

	filters := Filters()
	for i := len(filters) - 1; i >= 0; i-- {
		next = filters[i].DoFilter(next)
	}

	if err := next(ctx); err != nil {
		s.Writer.WriteErr(ctx, err)
	}
	return nil
}

// handle 处理器
func (s *Server) handle(ctx InnerContext) *ServerError {

	transporter, ok := GetTransporter(ctx.Service().Protocol)
	if !ok {
		return NewServerError(200, "500", "no transporter found")
	}

	resp, err := transporter.DoInvoke(ctx)

	select {
	case <-ctx.Context().Done():
		return NewServerError(http.StatusBadRequest, "400", "Request canceled")
	default:
		if helper.IsNil(err) {
			s.Writer.Write(ctx, resp)
			return nil
		}
		return err
	}
}
